#include "Skygen/SphericalMapSkyboxGenerator.h"

#include "Core/Kernel.h"
#include "GfxDriver/GfxDriver.h"

#include "Image/Image.h"
#include "Image/ImageService.h"

namespace Orca {

	//--------------------------------------------------------------------------
	TSphericalMapSkyboxGenerator::TSphericalMapSkyboxGenerator()
		: mNoiseTex(NULL)
		, mCloudTexSize(256)
		, mAccumTime(0.0f) {

		mCPhi	= new float[mCloudTexSize];
		mSPhi	= new float[mCloudTexSize];

		mCTheta = new float[mCloudTexSize];
		mSTheta = new float[mCloudTexSize];

		float stepSize = 1.0f / mCloudTexSize;
		float t = 0.0f;
		
		for(uint32 i = 0; i < mCloudTexSize; ++i) {
			
			float phi = t * Math::PI;						// [0, 180], polar angle
			float theta = t * (2.0f * Math::PI);			// [0, 360]

			mCPhi[i] = Math::cos(phi);
			mSPhi[i] = Math::sin(phi);
			
			mCTheta[i] = Math::cos(theta);
			mSTheta[i] = Math::sin(theta);

			t += stepSize;
		}
	}

	//--------------------------------------------------------------------------
	TSphericalMapSkyboxGenerator::~TSphericalMapSkyboxGenerator() {

		delete mNoiseTex;

		delete[] mCPhi;
		delete[] mSPhi;
		delete[] mCTheta;
		delete[] mSTheta;
	}

	//--------------------------------------------------------------------------
	Str TSphericalMapSkyboxGenerator::getVertexProgramFilename() {

		return "Skygen/shader/Skybox_SphericalMap_vp.glsl";
	}

	//--------------------------------------------------------------------------
	Str TSphericalMapSkyboxGenerator::getFragmentProgramFilename() {

		return "Skygen/shader/Skybox_SphericalMap_fp.glsl";
	}

	//--------------------------------------------------------------------------
	vec4 TSphericalMapSkyboxGenerator::computeSkyboxColor(const vec3 &dir, int size) {

		return vec4(0.0f, 0.0f, 0.0f, 1.0f);
	}

	//--------------------------------------------------------------------------
	void TSphericalMapSkyboxGenerator::updateShaderConstants(Shader *shader) {

		shader->beginBinding();

		if(mNoiseTex)
			shader->bindTexture("g_CloudTex", mNoiseTex);

		shader->bindVec2("g_TexOffset", vec2(mAccumTime, 0.0f));

		shader->endBinding();
	}

	//--------------------------------------------------------------------------
	void TSphericalMapSkyboxGenerator::step(float dt) {

		if(!mNoiseTex) {
	
			mNoiseTex = INST<GfxDriver>()->create2DTexture(
				mCloudTexSize,
				mCloudTexSize,
				PF_INT8_RGBA);

			uint8 *dataPtr = (uint8*)mNoiseTex->requestUpload();
		
			for(uint32  y = 0; y < mCloudTexSize; ++y) {

				float cPhi = mCPhi[y];
				float sPhi = mSPhi[y];
				
				for(uint32 x = 0; x < mCloudTexSize; ++x) {

					float cTheta = mCTheta[x];
					float sTheta = mSTheta[x];

					vec3 dir(
						sPhi * cTheta,
						sPhi * sTheta,
						cPhi);

					float scale[] = {1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f};

					float noise = 0.0f;

					for(int i=0; i<6; ++i) {

						vec3 pos = dir * scale[i] * 3.0f;
						float n = mIPN.pnoise( pos[0], pos[1], pos[2] );

						noise += n * (1.0f / scale[i]);
					}

					float noise2 = Math::clamp(1.0f * noise, 0.0f, 1.0f);

					// cloud rim light
					float shading = 1 - (noise2 * 1.0f);
					shading = Math::pow(shading, 8.0f);
					shading = Math::clamp(shading, 0.0f, 1.0f);

					float masking = noise + 0.1f;// * 2.0f + 0.2f;
					masking = Math::clamp(masking, 0.0f, 1.0f);
					
					dataPtr[0] = uint8(noise2 * 255.0f);
					dataPtr[1] = uint8(masking * shading * 255.0f);
					dataPtr[2] = 255;
					dataPtr[3] = 255;

					dataPtr += 4;
				}
			}

			mNoiseTex->uploadToGPU(false);
		}

		mAccumTime += dt * 0.0001f;
	}

	//--------------------------------------------------------------------------

}